package my.bc.webdav.client;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import my.bc.webdav.model.DavFile;
import my.bc.webdav.model.DavFold;
import my.bc.webdav.model.DavNode;

import org.apache.commons.httpclient.HttpException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DavUtil
{
  private static final Logger log = LoggerFactory.getLogger( DavSession.class );
  
  /**
   * copy resources from src server to dst server
   * There are two ways to implement this method.
   *   - First way is get the metadata information of src and dst resources, and then compare to a list of operation, such as which folds/files
   *     need to be created/deleted/copied etc. The disadvantage of this way is the dst resources can not be multiple re-entry during the whole
   *     operation period. The advantage is it can probably take advantage of batch  or optimized operation to increase performance.
   *   - Second way is do it one level after another.     
   * 
   * @param srcServer
   * @param srcPath
   * @param dstServer
   * @param dstPath
   * @param foldConflictOperation
   * @param fileConflictOperation: this flag only useful when foldConflictOperation is merge. or the resource pointed by srcPath is a file
   * @param recursively
   */
  public static void copyResource( ServerInfo srcServer, String srcPath, ServerInfo dstServer, String dstPath, 
                                   ConflictOperation foldConflictOperation, ConflictOperation fileConflictOperation, boolean recursively )
  {
    DavSession srcSession = new DavSession( srcServer );
    DavSession dstSession = new DavSession( dstServer );

    try
    {
      //get skeleton of src server
      DavNode srcNode = srcSession.getSkeleton( srcPath, recursively );
//      DavNode dstNode = dstSession.getSkeleton( dstPath, recursively );
//      List< DavOperation > davOperations = getDavOperations( srcNode, dstNode, foldConflictOperation, fileConflictOperation, true );
//      
//      //create skeleton
//      execute( davOperations );
      
      //copy resources
      copyResource( srcSession, srcNode, srcPath, dstSession, dstPath );
    }
    catch( Exception e )
    {
      e.printStackTrace();
    }
  }
  
  /**
   * This method support no conflict at all, all the conflict should be handled before calling this method.
   * 
   * @param srcSession
   * @param srcNode
   * @param dstSession
   * @param dstNode
   * @throws IOException 
   * @throws URISyntaxException 
   * @throws HttpException 
   */
  public static void copyResource( DavSession srcSession, DavNode srcNode, String srcPath, 
                                   DavSession dstSession, String dstPath ) 
                                   throws HttpException, URISyntaxException, IOException
  {
    if( srcNode instanceof DavFile )
    {
      copyFile( srcSession, srcPath, dstSession, dstPath );
    }
    else
    {
      copyFold( srcSession, srcPath, (DavFold)srcNode, dstSession, dstPath, true );
    }

  }
  
  //TODO: to much to think over, handle later
  public static List< DavOperation > getDavOperations( DavNode srcNode, DavNode dstNode, 
                                                ConflictOperation foldConflictOperation, ConflictOperation fileConflictOperation,
                                                boolean recursive )
  {
    List< DavOperation > operations = new ArrayList< DavOperation >();
    if( dstNode == null )
    {
      DavOperation operation = new DavOperation( srcNode, DavOperator.Create );
      operations.add( operation );
      return operations;
    }
    if( srcNode == null )
    {
      if( ConflictOperation.Overwrite.equals( foldConflictOperation ) )
      {
        operations.add( new DavOperation( dstNode, DavOperator.Delete ) );
      }
      return operations;
    }
    if( isConflict( srcNode, dstNode ) )
    {
      //conflict
      if( ConflictOperation.Overwrite.equals( foldConflictOperation ) )
      {
        //for overwrite fold, we can remove this fold and create a new empty one
        operations.add( new DavOperation( dstNode, DavOperator.Delete ) );
        
        //as we will delete all dstNode, need to handle sub nodes
        return operations;
      }

      if( ConflictOperation.KeepOriginal.equals( foldConflictOperation ) )
      {
        // don't need to copy the srcNode in order to keep the original
        srcNode.setValid( false );
        return operations;
      }

    }
    
    //TODO: to much to think over, handle later
    return operations;
  }
  
  //handle later
  public static void execute( List< DavOperation > davOperations )
  {
    
  }
  
  /**
   * it is allowed to create a file which has same name with fold
   * @param srcNode
   * @param dstNode
   * @return
   */
  protected static boolean isConflict( DavNode srcNode, DavNode dstNode )
  {
    return ( srcNode.getName().equals( dstNode.getName() ) && srcNode.getNodeType().equals( dstNode.getNodeType() ) ); 
  }
  
  /**********************
  public static void copyResource( ServerInfo srcServer, String srcPath, ServerInfo dstServer, String dstPath, 
                                   ConflictOperation foldConflictOperation, ConflictOperation fileConflictOperation, boolean recursively )
  {
    DavSession srcSession = new DavSession( srcServer );
    DavSession dstSession = new DavSession( dstServer );

    try
    {
      //get structure of src server
      DavNode srcNode = srcSession.getResource( srcPath, recursively );
      if( srcNode instanceof DavFile )
      {
        copyFile( srcSession, srcPath, (DavFile)srcNode, dstSession, dstPath, fileConflictOperation );
      }
      else
      {
        copyFold( srcSession, srcPath, (DavFold)srcNode, dstSession, dstPath, foldConflictOperation, fileConflictOperation, recursively );
      }
    }
    catch( Exception e )
    {
      e.printStackTrace();
    }
  }
  /**********************/
  
  public static void copyFile( DavSession srcSession, String srcPath, DavSession dstSession, String dstPath ) 
      throws HttpException, URISyntaxException, IOException
  {
    log.info( "Copying file: " + srcPath );
    //download the file and then upload it
    
    final String localFilePath = "c:\\tmp\\download." + Calendar.getInstance().getTimeInMillis() + ".tmp";
    
    if( !srcSession.downloadFile( srcPath, localFilePath ) )
    {
      log.warn( "Download file failed. file path: " + srcPath );
      return;
    }
    
    File localFile = new File( localFilePath );
    dstSession.putFile( dstPath, localFile );
    
    localFile.delete();
    log.info( "Done copying file: " + srcPath );
  }
  
  public static void copyFold( DavSession srcSession, String srcPath, DavFold srcFold, DavSession dstSession, String dstPath,
                               boolean recursively ) 
      throws HttpException, URISyntaxException, IOException
  {
    log.info( "Copying fold: " + srcPath );

    makeDirectory( dstSession, dstPath );
    
    for( DavNode subNode : srcFold.getChildren() )
    {
      if( subNode instanceof DavFile )
      {
        copyFile( srcSession, srcPath + "/" + subNode.getName(), dstSession, dstPath + "/" + subNode.getName() );
      }
      else
      {
        copyFold( srcSession, srcPath + "/" + subNode.getName(), (DavFold)subNode, dstSession, dstPath + "/" + subNode.getName(), true );
      }
    }
    log.info( "Done copying fold: " + srcPath );
  }
  
  public static void makeDirectory( DavSession session, String foldPath ) 
      throws HttpException, URISyntaxException, IOException
  {
    session.makeDirectory( foldPath );
  }
}
